/**
 * Date: 2021/12/22
 * Author: january
 * Description: 
 */

#include <stdio.h>
#include <libecc/libsig.h>
#include <libecc/utils/log.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <libecc/sig/sm2.h>
#include <string.h>
#include <libecc/def.h>

void encrypt_test() {
    int ret = 0;
    uint8_t msg[128];
    uint8_t cipher[128 + 64 + 32];
    uint8_t decrypt[128];

    int extra_data_len = 16;
    int msg_len = 128;
    int cipher_len = 128 + 64 + 32;
    int decrypt_len = 128;

    ec_key_pair keypair;
    ec_params ec;
    const ec_sig_mapping* sm2 = get_sig_by_type(SM2);

    const char* head = \
    "###############################\n" \
    "        encrypt test           \n" \
    "###############################";

    NOTICE("%s", head);

    local_memset(msg, 0x6, 128);
    local_memset(decrypt, 0x0, 128);

    // 生成密钥对
    import_params(&ec, ec_get_curve_params_by_type(SM2P256V1));
    ret = ec_key_pair_gen(&keypair, &ec, SM2);
    if (ret < 0) {
        LOG_ERROR("Fail to gen keypair");
        goto out;
    }
    LOG_INFO_MEM(msg, msg_len, "msg");
    // 加密
    cipher_len = sm2->encrypt(SM3, &(keypair.pub_key), msg, msg_len, cipher, cipher_len);
    if (cipher_len < 0) {
        LOG_ERROR("Fail to encrypt data");
        goto out;
    }
    LOG_INFO_MEM(cipher, cipher_len, "cipher");
    // 解密
    decrypt_len = sm2_decrypt(SM3, &(keypair.priv_key), cipher, cipher_len, decrypt, decrypt_len);
    if (decrypt_len < 0) {
        LOG_ERROR("Fail to decrypt data");
        goto out;
    }
    LOG_INFO_MEM(decrypt, decrypt_len, "decrypt");
    out:
    return;
}

void sign_test() {
    int ret = 0;
    uint8_t extra_data[16];
    uint8_t msg[128];
    uint8_t sig[128];
    int extra_data_len = 16;
    int msg_len = 128;
    int sig_len = 128;
    struct ec_sign_context ctx;
    ec_key_pair keypair;
    ec_params ec;
    const ec_sig_mapping* sm2 = get_sig_by_type(SM2);

    local_memset(extra_data, 0x5, 16);
    local_memset(msg, 0x6, 128);
    local_memset(sig, 0x0, 128);

    const char* head = \
    "###############################\n" \
    "           sign test           \n" \
    "###############################";

    NOTICE("%s", head);

    import_params(&ec, ec_get_curve_params_by_type(SM2P256V1));
    ret = ec_key_pair_gen(&keypair, &ec, SM2);
    if (ret < 0) {
        LOG_ERROR("Fail to gen keypair");
        goto out;
    }
    ret = ec_sign_init(&ctx, &keypair, SM2, SM3, extra_data, extra_data_len);
    if (ret < 0) {
        LOG_ERROR("Fail to init sign");
        goto out;
    }
    ret = ec_sign_update(&ctx, msg, msg_len);
    if (ret < 0) {
        LOG_ERROR("Fail to update sig");
        goto out;
    }
    ret = ec_get_sig_len(&ec, SM2, SM3, &sig_len);
    if (ret < 0) {
        LOG_ERROR("Fail to get sig len");
        goto out;
    }
    ret = ec_sign_finalize(&ctx, sig, sig_len);
    if (ret < 0) {
        LOG_ERROR("Fail to get sig");
        goto out;
    }
    LOG_INFO_MEM(sig, sig_len, "sig len:%d", sig_len);

    ret = ec_verify(sig, sig_len, &(keypair.pub_key), msg, msg_len, SM2, SM3, extra_data, extra_data_len);
    LOG_INFO("verify result %d", ret);

    out:
    return;
}

EVP_PKEY* openssl_load_pkey(const char* filename, const char* type) {
    EVP_PKEY* pkey = NULL;
    int ret = -1;
    int* p;
    FILE* file;

    // 读取密钥
    file = fopen(filename, "rb");
    if (file == NULL) {
        LOG_ERROR("Fail to open enc pkey file, %s", filename);
        return NULL;
    }

    EC_KEY* enc_ec_key = NULL;
    if (strcmp(type, "pub") == 0) {
        enc_ec_key = PEM_read_EC_PUBKEY(file, NULL, NULL, NULL);
    } else {
        enc_ec_key = PEM_read_ECPrivateKey(file, NULL, NULL, NULL);
    }
    if (enc_ec_key == NULL) {
        LOG_ERROR("Fail to read sm2 ec key_desc");
        return NULL;
    }
    pkey = EVP_PKEY_new();
    if (pkey == NULL) {
        EC_KEY_free(enc_ec_key);
        LOG_ERROR("Fail to creat empty pkey");
        return NULL;
    }
    ret = EVP_PKEY_set1_EC_KEY(pkey, enc_ec_key);
    if (ret != 1) {
        EC_KEY_free(enc_ec_key);
        LOG_ERROR("Fail to pass ec key_desc to pkey");
        return NULL;
    }
    EC_KEY_free(enc_ec_key);
    p = (int*)pkey;
    *p = EVP_PKEY_SM2;
    return pkey;

}

void openssl_sm2_enc_compatbility_test() {
    int ret;
    EVP_PKEY* pkey;
    EVP_PKEY_CTX* pkey_ctx;
    uint8_t msg[128];
    uint8_t cipher[256];
    uint8_t decrypt[128];
    int msg_size = 128;
    size_t cipher_size;
    int decrypt_size = 128;


    memset(msg, 0x3, 128);

    pkey = openssl_load_pkey("test.priv", "priv");
    if (pkey == NULL) {
        LOG_ERROR("Fail to load enc pkey");
        return;
    }
    pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL);
    if (pkey_ctx == NULL) {
        LOG_ERROR("Fail to alloc pkey pkey_ctx for encryption");
        return;
    }
    if (EVP_PKEY_encrypt_init(pkey_ctx) != 1) {
        LOG_ERROR("Fail to init");
        return;
    }

    if (EVP_PKEY_encrypt(pkey_ctx, cipher, &cipher_size, msg, msg_size) != 1) {
        LOG_ERROR("Fail to encrypt");
        return;
    }

    ec_params ec;
    ec_key_pair ec_key;
    FILE* priv_key_file = NULL;
    uint8_t* pem_buf[512];
    uint8_t* der_buf[512];
    uint8_t cipher_buffer[512];
    int key_size;

    priv_key_file = fopen("test.priv", "rb");
    key_size = fread(pem_buf, 1, 512, priv_key_file);
    if (key_size <= 0) {
        LOG_ERROR("No key readed");
        return;
    }
    key_size = pem2der(pem_buf, key_size, der_buf, 512);
    import_params(&ec, ec_get_curve_params_by_type(SM2P256V1));
    ret = sm2_import_sec1privkey(&ec_key, &ec, der_buf, key_size);
    if (ret < 0) {
        LOG_ERROR("Fail to import key");
        return;
    }
    ret = sm2_convert_openssl_enc(cipher, cipher_size, cipher_buffer, 512);
    if (ret < 0) {
        LOG_ERROR("Fail to convert cipher");
        return;
    }
    ret = sm2_decrypt(SM3, &(ec_key.priv_key), cipher_buffer, ret, decrypt, decrypt_size);
    if (ret < 0) {
        LOG_ERROR("Fail to decrypt data");
        return;
    }
    LOG_INFO_MEM(decrypt, ret, "decrypted data:");
    if (memcmp(decrypt, msg, msg_size) == 0) {
        LOG_INFO("openssl sm2 encryption compatibiliy ok");
    }
}

void openssl_sm2_sig_compatbility_test() {
    int ret;
    uint8_t* sig_buffer;
    EVP_PKEY* pkey;
    EVP_PKEY_CTX* pkey_ctx;
    uint8_t extra_data[16];
    int sig_size;
    int digest[32];
    int digest_size = 32;

    memset(digest, 0x3, 32);

    pkey = openssl_load_pkey("test.priv", "priv");
    if (pkey == NULL) {
        LOG_ERROR("Fail to load sig key");
        return;
    }
    pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL);
    if (pkey_ctx == NULL) {
        LOG_ERROR("Fail to alloc pkey ctx");
        return;
    }
    ret = EVP_PKEY_sign_init(pkey_ctx);
    if (ret != 1) {
        LOG_ERROR("Fail to init sign");
        return;
    }
    ret = EVP_PKEY_sign(pkey_ctx, NULL, &sig_size, digest, digest_size);
    if (ret != 1) {
        LOG_ERROR("Fail to get sig size");
        return;
    }
    sig_buffer = (uint8_t*)malloc(sig_size);
    if (sig_buffer == NULL) {
        LOG_ERROR("Fail to alloc sig buffer");
        return;
    }
    ret = EVP_PKEY_sign(pkey_ctx, sig_buffer, &sig_size, digest, digest_size);
    if (ret != 1) {
        LOG_ERROR("Fail to sign data");
        return;
    }
    EVP_PKEY_free(pkey);

    ec_pub_key pubkey;
    ec_params ec;
    struct ec_verify_context vctx;
    const ec_sig_mapping* sm2 = get_sig_by_type(SM2);
    FILE* pubkey_file;
    uint8_t pem_buf[512];
    uint8_t der_buf[512];
    uint8_t sig[512];
    int key_size;

    import_params(&ec, ec_get_curve_params_by_type(SM2P256V1));
    pubkey_file = fopen("test.pub", "rb");
    key_size = fread(pem_buf, 1, 512, pubkey_file);
    key_size = pem2der(pem_buf, key_size, der_buf, 512);
    ret = sm2_import_subpubkey(&pubkey, &ec, der_buf, key_size);
    if (ret < 0) {
        LOG_ERROR("Fail to load ec pub key");
        return;
    }
    sig_size = sm2_convert_openssl_sig(sig_buffer, sig_size, sig, 512);
    if (sig_size < 0) {
        LOG_ERROR("Fail to convert signature");
        return;
    }
    ec_verify_init(&vctx, &pubkey, sig, sig_size, SM2, SM3, NULL, 0);
    ret = _sm2_do_verify(&vctx, digest, digest_size);
    if (ret != 0) {
        LOG_ERROR("Invalid signature");
        return;
    }
    LOG_INFO("openssl sm2 signature compatibiliy ok");
}

void openssl_compatbility_test() {
    openssl_sm2_enc_compatbility_test();
    openssl_sm2_sig_compatbility_test();
}

void crypto_type_convert_test() {
    NOTICE("SEC_KEY_SM2_PUBKEY: [%s], [%x]", secrypto_key_type_to_str(SEC_KEY_SM2_PUBKEY), 
                            secrypto_str_to_key_type("SEC_KEY_SM2_PUBKEY"));

}

int main() {
    int ret = 0;
//    encrypt_test();
//    sign_test();
//    openssl_compatbility_test();
    crypto_type_convert_test();
    out:
    return 0;
}
